package com.kintreda.common.oauth.security;

import com.kintreda.common.mybatis.entity.SysMenu;
import com.kintreda.common.mybatis.service.ISysMenuService;
import com.kintreda.common.mybatis.service.ISysRoleMenuService;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;

/**
 * 权限拦截器
 * @Description: FilterInvocationSecurityMetadataSource（权限资源过滤器接口）继承了 SecurityMetadataSource（权限资源接口）
 * Spring Security是通过SecurityMetadataSource来加载访问时所需要的具体权限；Metadata是元数据的意思。
 * 自定义权限资源过滤器，实现动态的权限验证
 * 它的主要责任就是当访问一个url时，返回这个url所需要的访问权限
 */
@Component
public class AuthFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Autowired
    private ISysMenuService iSysMenuService;
    @Autowired
    private ISysRoleMenuService iSysRoleMenuService;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    private static final Logger log = LoggerFactory.getLogger(AuthFilterInvocationSecurityMetadataSource.class);


    /**
     * 返回本次访问需要的权限，可以有多个权限
     * @param o
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) o).getRequestUrl();
        //去掉路径中的问号后面部分
        String uri = requestUrl.substring(1,requestUrl.indexOf("?") >0 ? requestUrl.indexOf("?") : requestUrl.length()).replaceAll("/",":");
        //拆解URI，区分权限所在分组
        String[] split = uri.split(":");
        //第一个节点表示分组名称
        String group = split[0];
        //匹配权限路径
        SysMenu sysMenu = iSysMenuService.getAuthorizeByUrl(uri.replace(group+":",""),group);
        //数据库中记录有这个权限，必须要指定角色才能访问
        if (ObjectUtils.isNotEmpty(sysMenu)){
            //必须指定角色才能访问
            if (iSysRoleMenuService.getRoles(sysMenu.getMenuId()).size() > 0){
                Collection<String> roles = iSysRoleMenuService.getRoles(sysMenu.getMenuId());
                String[] values = roles.toArray(new String[roles.size()]);
                log.info("当前访问路径是{},这个url所需要的访问权限是{}", requestUrl, values);
                return SecurityConfig.createList(values);
            }
            //未指定角色
            log.info("当前访问路径是{},这个url所需要的访问权限是{}", requestUrl, "ROLE_MANAGER");
            return SecurityConfig.createList("ROLE_MANAGER");
        }


        /**
         * @Author: Galen
         * @Description: 如果本方法返回null的话，意味着当前这个请求不需要任何角色就能访问
         * 此处做逻辑控制，如果没有匹配上的，返回一个默认具体权限，防止漏缺资源配置
         **/
        log.info("当前访问路径是{},这个url所需要的访问权限是{}", requestUrl, "ROLE_LOGIN");
        return SecurityConfig.createList("ROLE_LOGIN");
    }

    /**
     *  此处方法如果做了实现，返回了定义的权限资源列表，
     *  Spring Security会在启动时校验每个ConfigAttribute是否配置正确，
     *  如果不需要校验，这里实现方法，方法体直接返回null即可。
     * @return
     */
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    /**
     *  方法返回类对象是否支持校验，
     *  web项目一般使用FilterInvocation来判断，或者直接返回true
     * @param aClass
     * @return
     */
    @Override
    public boolean supports(Class<?> aClass) {
        return FilterInvocation.class.isAssignableFrom(aClass);
    }
}
